/*
 * DissimilarityMatrix.cpp
 *
 *  Created on: Aug 25, 2012
 *      Author: alberto
 */

#include "DissimilarityMatrix.h"

DissimilarityMatrix::DissimilarityMatrix(const ImageContainer& group): Dissimilarity(),
                                                                       imageGroupKeypoints(),
                                                                       imageGroupDescriptors(),
                                                                       imageGroupMatches(){
	imageGroup = group;
}

DissimilarityMatrix::~DissimilarityMatrix() {
}

ImageContainer DissimilarityMatrix::getImageGroup() const{
	return imageGroup;
}

void DissimilarityMatrix::buildDissimilarity(){

	size_t imnumber = imageGroup.getImageNumber();
	//computeImageGroupKeypoints();

	Dissimilarity = Mat(imnumber, imnumber, CV_64F, -1);
	double totald = (double)getTickCount();
	//cout << "before: " << endl << Dissimilarity << endl;

	for(size_t i = 0; i < imnumber; i++){
		for(size_t j = 0; j < imnumber; j++){
			if(i != j){
				stringstream ss1;
				stringstream ss2;
				stringstream ss3;

				ss1 << i << ".jpeg - Original" << endl;
				ss2 << i << ".jpeg - Transformada" << endl;
				ss3 << j << ".jpeg - Comparada" << endl;
				double dt = (double)getTickCount();
				cout << endl << i << " - " << j << endl;
				cout << "dissimilarity between: " << imageGroup.getImageName(i) << " and " << imageGroup.getImageName(j) << endl;
				Mat imageA = imageGroup.getImages(i);
				Mat imageB = imageGroup.getImages(j);

				Mat affineTransformA;
				Mat finalTransformA;
				estimateAffineWarp(imageA, imageB, affineTransformA);
				histogramEspecification(affineTransformA, imageB);
				estimateCompression(affineTransformA, imageB, imageGroup.getImagePath(j), finalTransformA);
				//imshow(ss1.str(), imageA);
				//imshow(ss2.str(), finalTransformA);
				//imshow(ss3.str(), imageB);
				//moveWindow(ss1.str(), 50, 200);
				//moveWindow(ss2.str(), 600, 100);
				//moveWindow(ss3.str(), 1150, 100);
				//waitKey(0);
				//destroyAllWindows();
				string name = imageGroup.getImagePath(i) + "_TO_" + imageGroup.getImagePath(j);
				//imwrite(name, affineTransformA);
				double r = ComputeImageCorrelation(finalTransformA, imageB, ss2.str(), ss3.str());
				Dissimilarity.at<double>(i, j) = r;
				dt = ((double)getTickCount() - dt)/getTickFrequency();
				//cout << "Dissimilarity time: " << dt << endl;
				//cout << endl << endl;
			}
		}
	}

	totald = ((double)getTickCount() - totald)/getTickFrequency();
	//cout << "Mat time: " << totald << endl << endl;

	//cout << "D Matrix" << endl << Dissimilarity << endl;
	return;
}

void DissimilarityMatrix::computeImageGroupKeypoints(){

	size_t ni = imageGroup.getImageNumber();

	SURF detector(2500);

	cerr << endl << "calculating keypoints and descriptors: " << endl << endl;
	for(size_t i=0; i<ni; i++){
		vector<KeyPoint> keypoints;
		Mat descriptors;

		Mat image = imageGroup.getImages(i);
		string imname = imageGroup.getImagePath(i);

		cerr << "computing keypoints and descriptors for image: " << imname << endl;
		detector(image, Mat(), keypoints, descriptors);
		cerr << "Number of keypoints: " << keypoints.size() << endl;

		imageGroupKeypoints.push_back(keypoints);
		imageGroupDescriptors.push_back(descriptors);
	}
	cerr << endl;
	return;
}


void DissimilarityMatrix::estimateAffineWarp(const Mat& imageA, const Mat& imageB, Mat& transformedA){

	cout << "Estimate Affine warp" << endl;
	double dt = (double)getTickCount();
	Mat eqA;
	Mat eqB;
	Mat resA;
	//equalizeColorImage(imageA, eqA);
	//equalizeColorImage(imageB, eqB);

	resize(imageA, resA, imageB.size(), 0, 0, INTER_LINEAR);
	Mat affTransform = estimateRigidTransform(resA, imageB, true);
	warpAffine(resA, transformedA, affTransform, transformedA.size());

	/*imshow("A", resA);
	imshow("B", imageB);
	imshow("Aff", transformedA);
	waitKey(0);
	destroyAllWindows();*/

	dt = ((double)getTickCount() - dt)/getTickFrequency();
	//cout << "Affine warp time: " << dt << endl << endl;
	return;

}

void DissimilarityMatrix::histogramEspecification(Mat& imageSrc, Mat& imageTarget){

	cout << "Match color" << endl;
	Mat res;
	matchColor(imageSrc, imageTarget, res);
	res.copyTo(imageSrc);

	return;

	double dt = (double)getTickCount();
	int channels[] = {0};
	int size[] = {256};
	float fran[] = {0, 256};
	const float* ranges[] = {fran};

	vector<Mat> channelsSrc;
	vector<Mat> channelsTarget;

	imwrite("A_original.jpeg", imageSrc);
	imwrite("B_original.jpeg", imageTarget);
	split(imageSrc, channelsSrc);
	split(imageTarget, channelsTarget);

	assert(channelsSrc.size() == channelsTarget.size());

	for(size_t i = 0; i < channelsSrc.size(); i++){
		Mat hist_Src;
		Mat hist_Target;
		Mat dst;
		vector<double> cdf_Src;
		vector<double> cdf_Target;
		vector<size_t> eq_mapping;
		vector<size_t> match_mapping;

	    calcHist(&channelsSrc[i], 1, channels, Mat(), hist_Src, 1, size, ranges, true);
	    calcHist(&channelsTarget[i], 1, channels, Mat(), hist_Target, 1, size, ranges, true);

	    calculateCumulativeDistribution(hist_Src, channelsSrc[i].total(), cdf_Src);
	    calculateCumulativeDistribution(hist_Target, channelsTarget[i].total(), cdf_Target);
	    createHistogramMatchingMap(cdf_Src, cdf_Target, match_mapping);
	    histogramMatching(channelsSrc[i], eq_mapping, match_mapping, dst);
	}

	imageSrc = Mat();
	merge(channelsSrc, imageSrc);

	/*imshow("Source", imageSrc);
	imshow("Target", imageTarget);
	waitKey(0);
	destroyAllWindows();*/
	dt = ((double)getTickCount() - dt)/getTickFrequency();
	//cout << "Histogram matching time: " << dt << endl << endl;

	imwrite("A_transformada.jpeg", imageSrc);
	return;

}

void DissimilarityMatrix::estimateCompression(const Mat& imageA, const Mat& imageB, string nameB, Mat& recompressedA){

	cout << "Recompress image" << endl;
	double dt = (double)getTickCount();
	//cout << "Recompressing: ";
	Mat invertRecompressedA;
	jpegRecompression(imageA, nameB, invertRecompressedA);
	cvtColor(invertRecompressedA, recompressedA, CV_RGB2BGR);
	dt = ((double)getTickCount() - dt)/getTickFrequency();
	//cout << "Recompression time: " << dt << endl << endl;
	return;
}

double DissimilarityMatrix::ComputeImageCorrelation(const Mat& imageA, const Mat& imageB, string nameA, string nameB){

	double dt = (double)getTickCount();
	cout << "Compute Image Dissimilarity: ";
	vector<Mat> channelsA;
	vector<Mat> channelsB;
	Mat HSVimageA;
	Mat HSVimageB;

	//imshow("Transformada", imageA);
	//imshow("Alvo", imageB);
	//waitKey(0);
	//destroyAllWindows();

	//cvtColor(imageA, HSVimageA, CV_RGB2HSV);
	//cvtColor(imageB, HSVimageB, CV_RGB2HSV);

	split(imageA, channelsA);
	split(imageB, channelsB);


	double r_b = correlationCoefficient(channelsA[0], channelsB[0]);
	double r_g = correlationCoefficient(channelsA[1], channelsB[1]);
	double r_r = correlationCoefficient(channelsA[2], channelsB[2]);

	//double r_b = meanSqrdError(channelsA[0], channelsB[0]);
	//double r_g = meanSqrdError(channelsA[1], channelsB[1]);
	//double r_r = meanSqrdError(channelsA[2], channelsB[2]);

	double r = (r_b + 2*r_g + r_r)/4;

	cout << r << endl;
	dt = ((double)getTickCount() - dt)/getTickFrequency();
	//cout << "Dissimilarity compute time: " << dt << endl << endl;
	return r;
}

Mat DissimilarityMatrix::getDissimilarity(){
	return Dissimilarity;
}

